#! /usr/bin/python

# bird_up.py

"""\nBlupy Python bird-list upload program


Options:
  -h, --help              show this help
  -d ..., --database=...  specify a database (defaults to birdlist)
  -t ..., --table=...     specify a table within the database (defaults to lifeList)
  -c ..., --country=...   specify which country you would like to set as default
                          if the countries name is two or more words, wrap them in quotes - 'South Africa'
  -y ..., --year=...      specify the year

You can also run this as bird.py.
run:

python bird.py -h to see options

While running the program, you can change the year, place and country
setting preset values that remain as long as you run the program.
Click on the set menu for these options.

Note that if you change the database or table,
those new ones are used from then on until you specifically change it.

Basically, it is trying to make the tedious task of getting your hand-written
bird lists into the computer as easy as possible.
The hacking of a lazy birder

The database takes these values:
    Common name: Common name of the birds.  Don't worry about capitalization.  It'll do that for you.
    Scientific name: Again, it will do all the work, just put the words in
    Day:  numerals
    Month:  You can enter the full name, a three letter abbreviation, or the number of the month
    Year:  Enter the full year
    Place: Give some detial if you want
    Town:  If you want it
    Country:  If you want it
    Notes:  Give some info...down the line these little notes really connect you back to that time.
    Images:  not yet supported, coming soon
    
All the values are stored in a sqlite database, but you can create an html page with 'Build HTML'

This program comes with ABSOLUTELY NO WARRANTY.
    This is free software, and you are welcome to redistribute it
    under certain conditions.\n"""

#To do:
#    Organize packaging properly...early because it will change the code a bit.
#    work on __doc__ and help dialog
#    Improve the look and feel of the html page
#    Work on incorporating a spell checker (probably a big job)
#    When I really have nothing to do [never] add tooltips for EVERYTHING
#    Make it so that users can bring in a birdlist
#    --I would need to make it so that the birdlist can be read into a database
#    --Then I could provide optionally drop down menues for the common name/sci names.
#    --It should always remain optional.
#    We could really simply make an option to switch the day and month for Americans?
#    Bug - if I upload with the Bird_upFtp.py module, it doesn't build the html file
#    --Just make it call the birdHtml.py methods.
#    Add a blog post uploader that automatically links to the birdlist.
#    It would be nice to add some functionality where several people can add lists
#    --And write a page for doing statistical comparisons or drawing up graphs or tables.
#    --adding that is not trivial and would be best done together with someone who already does work like that

def copyrightText():
    from time import localtime
    if localtime()[0] == 2009:
        crD = "2009"
    else:
        crD = "2009 - %d" % localtime()[0]
    return crD

__author__ = "Vernon Swanepoel (vernon@Sandcurves.com)"
__version__ = "Version:0.1a_or_SparksStillFlying"
__date__ = "December 2009"
__copyright__ = "Copyright(c) %s Vernon Swanepoel" % copyrightText()


noWxpy = """The wxPython module is required to run this program.
You may choose to download it:

http://www.wxpython.org/download.php

or rather the console based bird.py."""

noSqlite3 = """The Sqlite3 database program is not installed on your system.

You can run:
sudo apt-get install sqlite3
on a Linux system to download and install sqlite3
Sqlite3 is bundled with Python, and if you have python
you should also have Sqlite3."""

try:
    import wx
except ImportError:
    raise ImportError, noWxpy

from bird import EachBird
try:
    import sqlite3 as lite
except:
    raise ImportError, noSqlite3
import sys
import os
import getopt

class BirdUpload(wx.Frame, EachBird):
    """Bird Upload extends my EachBird class (bird.py module)
    
    The class provides a gui framework for working with bird list uploading.
    """
    def __init__(self, parent, id, title='Blupy - Bird List Uploader', dbase='', table='',
                 presetCountry='', presetYear=''):
        wx.Frame.__init__(self, parent, id, title, size=(620, 720))
        EachBird.__init__(self, dbase, table)
        
        # Making sure that the tables we call do exist is done by the EachBird module
        
        # If the user wants to preset values, the options are:
        # Country - from the menu or from the console
        # Year  - from the menu or from the console
        # Place - Usually only needed for shorter stretches,
        #         place is only available from the menu.
        self.prStCntry = presetCountry
        self.prStYr = str(presetYear)
        self.prStPlace = ""
        
        # Set the fonts
        self.font = wx.SystemSettings_GetFont(wx.SYS_SYSTEM_FONT)
        self.font.SetPointSize(11)
        
        # Set a value for buttons with variable withs
        self.button_width = 70
        
        #Set a counter which selects the okay, rather than edit methods
        self.counter = 0
        
        # Dealing with the menus
        menubar = wx.MenuBar()
        # file menu
        file = wx.Menu()
        quit = wx.MenuItem(file, -1, '&Quit\tCtrl+Q')
        file.AppendItem(quit)
        self.Bind(wx.EVT_MENU, self.OnQuit, id=quit.GetId())
        # edit menu
        edit = wx.Menu()
        edit_ = wx.MenuItem(edit, -1, 'Edit')
        edit.AppendItem(edit_)
        self.Bind(wx.EVT_MENU, self.OnEdit, id=edit_.GetId())
        # set menu
        set = wx.Menu()
        self.year = wx.MenuItem(set, -1, '&Year')
        set.AppendItem(self.year)
        self.place = wx.MenuItem(set, -1, '&Place')
        set.AppendItem(self.place)
        self.country = wx.MenuItem(set, -1, '&Country')
        set.AppendItem(self.country)
        self.Bind(wx.EVT_MENU, self.YearDialogBox, id=self.year.GetId())
        self.Bind(wx.EVT_MENU, self.PlaceDialogBox, id=self.place.GetId())
        self.Bind(wx.EVT_MENU, self.CountryDialogBox, id=self.country.GetId())
        # help menu
        help = wx.Menu()
        blu_help = wx.MenuItem(help, -1, '&Blu_Help')
        help.AppendItem(blu_help)
        about = wx.MenuItem(help, -1, '&About')
        help.AppendItem(about)
        self.Bind(wx.EVT_MENU, self.HelpDialogBox, id=blu_help.GetId())
        self.Bind(wx.EVT_MENU, self.AboutDialogBox, id=about.GetId())
        # Get it all together
        menubar.Append(file, '&File')
        menubar.Append(edit, 'E&dit')
        menubar.Append(set, '&Set')
        menubar.Append(help, '&Help')
        self.SetMenuBar(menubar)
        
        # Start working on the gui framework
        self.panel = wx.Panel(self, -1)
        self.panel.SetBackgroundColour('#f57900')

        vbox = wx.BoxSizer(wx.VERTICAL)
        
        # First level - we just let the user know which bird was uploaded last
        hbox1 = wx.BoxSizer(wx.HORIZONTAL)
        try:
            most_recent = "The last bird you uploaded was %s" % EachBird.getMostRecent(self)
        except:
            most_recent =  "New database or table, with no data yet"
        self.last_bird = wx.StaticText(self.panel, -1, label=most_recent)
        self.last_bird.SetFont(self.font)
        hbox1.Add(self.last_bird, 0, wx.ALIGN_CENTER, 8)
        vbox.Add(hbox1, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 10)
        
        # Breathing room
        vbox.Add((-1, 25))        
        
        # Common name_
        hbox3 = wx.BoxSizer(wx.HORIZONTAL)
        self.common_name = wx.StaticText(self.panel, -1, "Common Name", size=(180, 30))
        self.common_name.SetFont(self.font)
        hbox3.Add(self.common_name, 0, wx.RIGHT | wx.TOP, 8)
        self.common_name_input = wx.TextCtrl(self.panel, -1, size=(400, 30))
        self.common_name_input.SetFocus()
        hbox3.Add(self.common_name_input, 1)        
        vbox.Add(hbox3, 0, wx.LEFT | wx.TOP, 10)
        
        # Scientific name_
        hbox4 = wx.BoxSizer(wx.HORIZONTAL)
        self.scientificName = wx.StaticText(self.panel, -1, "Scientific Name", size=(180, 30))
        self.scientificName.SetFont(self.font)
        hbox4.Add(self.scientificName, 0, wx.RIGHT | wx.TOP, 8)
        self.scientific_name_input = wx.TextCtrl(self.panel, -1, size=(400, 30))
        hbox4.Add(self.scientific_name_input, 1)        
        vbox.Add(hbox4, 0, wx.LEFT | wx.TOP, 10)
        
        # Day_
        hbox5 = wx.BoxSizer(wx.HORIZONTAL)
        self.day = wx.StaticText(self.panel, -1, "Day", size=(180, 30))
        self.day.SetFont(self.font)
        hbox5.Add(self.day, 0, wx.RIGHT | wx.TOP, 8)
        self.day_input = wx.TextCtrl(self.panel, -1, size=(400, 30))
        hbox5.Add(self.day_input, 1)        
        vbox.Add(hbox5, 0, wx.LEFT | wx.TOP, 10)
        
        # Month_
        hbox6 = wx.BoxSizer(wx.HORIZONTAL)
        self.month = wx.StaticText(self.panel, -1, "Month", size=(180, 30))
        self.month.SetFont(self.font)
        hbox6.Add(self.month, 0, wx.RIGHT | wx.TOP, 8)
        self.month_input = wx.TextCtrl(self.panel, -1, size=(400, 30))
        hbox6.Add(self.month_input, 1)        
        vbox.Add(hbox6, 0, wx.LEFT | wx.TOP, 10)
        
        # Year_
        hbox7 = wx.BoxSizer(wx.HORIZONTAL)
        self.year = wx.StaticText(self.panel, -1, "Year", size=(180, 30))
        self.year.SetFont(self.font)
        hbox7.Add(self.year, 0, wx.RIGHT | wx.TOP, 8)
        if self.prStYr == '':
            self.year_input = wx.TextCtrl(self.panel, -1, size=(400, 30))
        else:
            self.year_input = wx.TextCtrl(self.panel, -1, value=self.prStYr, size=(400, 30))            
        hbox7.Add(self.year_input, 1)        
        vbox.Add(hbox7, 0, wx.LEFT | wx.TOP, 10)        
        
        # Place_
        hbox8 = wx.BoxSizer(wx.HORIZONTAL)
        self.place = wx.StaticText(self.panel, -1, "Place", size=(180, 30))
        self.place.SetFont(self.font)
        hbox8.Add(self.place, 0, wx.RIGHT | wx.TOP, 8)
        if self.prStPlace == '':
            self.place_input = wx.TextCtrl(self.panel, -1, size=(400, 30))
        else:
            self.place_input = wx.TextCtrl(self.panel, -1, value=self.prStPlace, size=(400, 30))
        hbox8.Add(self.place_input, 1)        
        vbox.Add(hbox8, 0, wx.LEFT | wx.TOP, 10)        
        
        # Town_
        hbox9 = wx.BoxSizer(wx.HORIZONTAL)
        self.town = wx.StaticText(self.panel, -1, "Town", size=(180, 30))
        self.town.SetFont(self.font)
        hbox9.Add(self.town, 0, wx.RIGHT | wx.TOP, 8)
        self.town_input = wx.TextCtrl(self.panel, -1, size=(400, 30))
        hbox9.Add(self.town_input, 1)        
        vbox.Add(hbox9, 0, wx.LEFT | wx.TOP, 10)        
        
        # Country_
        hbox10 = wx.BoxSizer(wx.HORIZONTAL)
        self.country = wx.StaticText(self.panel, -1, "Country", size=(180, 30))
        self.country.SetFont(self.font)
        hbox10.Add(self.country, 0, wx.RIGHT | wx.TOP, 8)
        if self.prStCntry == '':
            self.country_input = wx.TextCtrl(self.panel, -1, size=(400, 30))
        else:
            self.country_input = wx.TextCtrl(self.panel, -1, value=self.prStCntry, size=(400, 30))            
        hbox10.Add(self.country_input, 1)        
        vbox.Add(hbox10, 0, wx.LEFT | wx.TOP, 10)        
        
        # Notes_
        hbox11 = wx.BoxSizer(wx.HORIZONTAL)
        self.Notes = wx.StaticText(self.panel, -1, "Notes", size=(180, 30))
        self.Notes.SetFont(self.font)
        hbox11.Add(self.Notes, 0, wx.RIGHT | wx.TOP, 8)
        self.notes_input = wx.TextCtrl(self.panel, -1, size=(400, 100), style=wx.TE_MULTILINE)
        hbox11.Add(self.notes_input, 1)        
        vbox.Add(hbox11, 0, wx.LEFT | wx.TOP, 10)
        
        # Ah! breath again
        vbox.Add((-1, 30))
        
        #Buttons
        hbox_buttons = wx.BoxSizer(wx.HORIZONTAL)
        self.okay_button = wx.Button(self.panel, -1, "OK", size=(90, 30))
        hbox_buttons.Add(self.okay_button, 0)
        html_button = wx.Button(self.panel, -1, "Build HTML", size=(110, 30))
        hbox_buttons.Add(html_button, 0)
        ftp_button = wx.Button(self.panel, -1, "Upload FTP", size=(110, 30))
        hbox_buttons.Add(ftp_button, 0)
        exit_button = wx.Button(self.panel, -1, "Close", size=(70, 30))
        hbox_buttons.Add(exit_button, 0)
        vbox.Add(hbox_buttons, 0, wx.ALIGN_RIGHT | wx.RIGHT, 10)
        self.Bind(wx.EVT_BUTTON, self.chooseMethod, id=self.okay_button.GetId())
        self.Bind(wx.EVT_BUTTON, self.getHtml, id=html_button.GetId())
        self.Bind(wx.EVT_BUTTON, self.getFtp, id=ftp_button.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnQuit, id=exit_button.GetId())
        self.okay_button.SetToolTip(wx.ToolTip("Click to set the value"))
        html_button.SetToolTip(wx.ToolTip("Click to build an HTML file with the bird list"))
        html_button.SetToolTip(wx.ToolTip("Click to upload the HTML file to the web"))
        exit_button.SetToolTip(wx.ToolTip("Exit"))
        
        # Place my panel
        self.panel.SetSizer(vbox)        
        
        # End of the structure part of the module
        self.statusbar = self.CreateStatusBar()
        self.statusbar.SetStatusText("Enter values for the next bird on your list and hit enter")
        self.Center()
        self.Show(True)
        
    def chooseMethod(self, event):
        """Normal mode this chooses to upload the next entry
        
        In edit mode, this chooses to replace the values that have
        been edited"""
        if self.counter == 0:
            self.getData()
        else:
            self.commitEdit()

    def getData(self):
        """Extending the getData method for the gui
        
        recieving the user input and storing it in values
        to be passed to the database"""        
        # data        
        self.bird_name = self.common_name_input.GetValue()
        self.bird_name = " ".join([nms.capitalize() for nms in self.bird_name.split()])
        self.scientific_name = self.scientific_name_input.GetValue()
        self.scientific_name.capitalize()
        self.date_observed = [self.day_input.GetValue(), self.month_input.GetValue(),
        self.year_input.GetValue()]
        self.date_observed[1] = EachBird.monthChecker(self, self.date_observed[1])
        self.PlaceTownCountry = [self.place_input.GetValue(),
        self.town_input.GetValue(),
        self.country_input.GetValue()]        
        self.notes = self.notes_input.GetValue()
        
        # Commit
        isThisWhatYouWant = """Is this the entry you wish to enter:
        
%s --> %s
%s --> %s
%s --> %s
%s
 --> %s
%s
 --> %s

Do you want to post these values?""" % ("Common Name",
                                        self.bird_name,
                                        "Scientific Name",
                                        self.scientific_name,
                                        "Date",
                                        " ".join(self.date_observed),
                                        "Place",
                                        " ".join(self.PlaceTownCountry),
                                        "Notes",
                                        self.notes)
        commitEntries = wx.MessageDialog(None, isThisWhatYouWant, 'Question', 
        wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
        if commitEntries.ShowModal() == wx.ID_YES:
            self.storeData()
            self.statusbar.SetStatusText("%s's data was uploaded to the database" % self.bird_name)
            self.last_bird.SetLabel("The last bird you uploaded was %s" % self.bird_name)
            
            #Reset values to start again
            self.common_name_input.SetValue("")
            self.scientific_name_input.SetValue("")
            self.common_name_input.SetFocus()        
            self.day_input.SetValue("")
            self.month_input.SetValue("")
            if self.prStYr == "":
                self.year_input.SetValue("")
            if self.prStPlace == "":
                self.place_input.SetValue("")
            self.town_input.SetValue("")
            if self.prStCntry == "":
                self.country_input.SetValue("")
            self.notes_input.SetValue("")
        else:
            choose_exit = wx.MessageDialog(None, "Would you like to exit", 'Question',
            wx.YES_NO | wx.ICON_QUESTION)
            if choose_exit.ShowModal() == wx.ID_NO:
                self.common_name_input.SetFocus()
            else:
                onQuit(event)
    
    def getHtml(self, event):
        """Simply build an HTML page from the birdlist database content"""
        # Lots to do here
        import birdHtml
        show = birdHtml.MakeHtml(self.dbase, self.table)
        show.showInBrowser()
        
    def getFtp(self, event):
        """Load an ftp file to the internet"""
        import Bird_upFtp
        _ftp = Bird_upFtp.BirdUpYear(None, -1)
        _ftp.ShowModal()
        _ftp.Destroy()
        self.common_name_input.SetFocus()
        
    def YearDialogBox(self, event):
        """Change or set a default year"""
        import Bird_upYear
        _year = Bird_upYear.BirdUpYear(None, -1)
        _year.ShowModal()
        self.year_input.SetValue(_year.yr)
        self.prStYr = _year.yr
        _year.Destroy()
        
    def PlaceDialogBox(self, event):
        """Change or set a default place"""
        import Bird_upPlace
        _place = Bird_upPlace.BirdUpPlace(None, -1)
        _place.ShowModal()
        self.place_input.SetValue(_place.pl)
        self.prStPlace = _place.pl
        _place.Destroy()
        
    def CountryDialogBox(self, event):
        """Change or set a default country"""
        import Bird_upCountry
        _country = Bird_upCountry.BirdUpCountry(None, -1)
        _country.ShowModal()
        self.country_input.SetValue(_country.ct)
        self.prStCntry = _country.ct
        _country.Destroy()
        
    def AboutDialogBox(self, event):
        """Content for the about dialog on the gui"""
        import Bird_upAbout
        _about = Bird_upAbout.BirdUpAbout(None, -1)
        _about.ShowModal()
        _about.Destroy()
        
    def HelpDialogBox(self, event):
        """Content for the help dialog on the gui"""
        import Bird_upHelp
        _help = Bird_upHelp.BirdUpHelp(None, -1)
        _help.ShowModal()
        _help.Destroy()
        
    def OnEdit(self, event):
        """Edit entries already posted"""
        import Bird_upEdit
        _edit = Bird_upEdit.BirdUpEdit(None, -1, self.dbase, self.table)
        _edit.ShowModal()
        try:
            self.RecieveValue = _edit.passback
            if len(self.RecieveValue) == 2:
                _edit.Destroy()
                con = lite.connect(self.dbase)
                cur = con.cursor()
                cur.execute('select * from %s where %s="%s";' % (self.table, self.RecieveValue[0], self.RecieveValue[1]))
                self.editVals = cur.fetchone()        
                cur.close()
                con.close()
                self.common_name_input.SetValue(self.editVals[1])
                self.common_name.SetLabel('Edit Common Name')
                self.scientific_name_input.SetValue(self.editVals[2])
                self.scientificName.SetLabel('Edit Scientific Name')
                self.common_name_input.SetFocus()        
                self.day_input.SetValue(self.editVals[3])
                self.day.SetLabel('Edit Day')
                self.month_input.SetValue(self.editVals[4])
                self.month.SetLabel('Edit Month')
                self.year_input.SetValue(self.editVals[5])
                self.year.SetLabel('Edit Year')
                self.place_input.SetValue(self.editVals[6])
                self.place.SetLabel('Edit Place')
                self.town_input.SetValue(self.editVals[7])
                self.town.SetLabel('Edit Town')
                self.country_input.SetValue(self.editVals[8])
                self.country.SetLabel('Edit Country')
                self.notes_input.SetValue(self.editVals[9])
                self.Notes.SetLabel('Edit Notes')
                self.statusbar.SetStatusText('Edit Mode')
                self.okay_button.SetLabel('Save Edit')
                self.last_bird.SetLabel("Edit Birdlist")
                self.counter = 1
        except:
            self.counter = 0
            _edit.Destroy()
            
        
            
        
    def commitEdit(self):
        # This counter is reset to zero.
        # It ensures that stuff is posted to the right place in the database
        self.counter = 0
        # Post the edits to the database
        self.bird_name = self.common_name_input.GetValue()
        self.bird_name = " ".join([nms.capitalize() for nms in self.bird_name.split()])
        self.scientific_name = self.scientific_name_input.GetValue()
        self.scientific_name.capitalize()
        self.date_observed = [self.day_input.GetValue(), self.month_input.GetValue(),
        self.year_input.GetValue()]
        self.date_observed[1] = EachBird.monthChecker(self, self.date_observed[1])
        self.PlaceTownCountry = [self.place_input.GetValue(),
        self.town_input.GetValue(),
        self.country_input.GetValue()]        
        self.notes = self.notes_input.GetValue()
        
        
        # Upload to database
        con = lite.connect(self.dbase)
        cur = con.cursor()        
        query = """update %s set cn="%s", sn="%s", day="%s", month="%s", year="%s", place="%s", town="%s", country="%s", notes="%s" where key=%d""" %\
        (self.table,\
         self.bird_name,\
         self.scientific_name,\
         self.date_observed[0],\
         self.date_observed[1],\
         self.date_observed[2],\
         self.PlaceTownCountry[0],\
         self.PlaceTownCountry[1],\
         self.PlaceTownCountry[2],\
         self.notes,\
         self.editVals[0])
        
        cur.execute(query)
        con.commit()
        cur.close()
        con.close()
        
        # Reset all the fields and labels and everything!
        self.common_name_input.SetValue("")
        self.scientific_name_input.SetValue("")
        self.common_name_input.SetFocus()
        self.day_input.SetValue("")
        self.month_input.SetValue("")
        if self.prStYr == "":
            self.year_input.SetValue("")
        self.place_input.SetValue("")
        self.town_input.SetValue("")
        if self.prStCntry == "":
            self.country_input.SetValue("")
        self.notes_input.SetValue("")        
        self.statusbar.SetStatusText('Edit Complete')
        self.okay_button.SetLabel('OK')
        self.common_name.SetLabel('Common Name')
        self.scientificName.SetLabel('Scientific Name')
        self.common_name_input.SetFocus()
        self.day.SetLabel('Day')
        self.month.SetLabel('Month')
        self.year.SetLabel('Year')
        self.place.SetLabel('Place')
        self.town.SetLabel('Town')
        self.country.SetLabel('Country')
        self.Notes.SetLabel('Notes')
        self.last_bird.SetLabel("The last bird you uploaded was %s" % EachBird.getMostRecent(self))

    def OnQuit(self, event):
        """Duh! Quit program"""
        self.Close()


if __name__ == "__main__":
    tb, db, country = "", "", ""
    yr = ''
    argv = sys.argv[1:]
    try:
        opts, args = getopt.getopt(argv, 'hd:t:c:y:', ['help', 'database=', 'table=', 'country=', 'year='])
    except getopt.GetoptError:
        print __doc__
        sys.exit()
    
    for opt, arg in opts:
        if opt in ('-h', '--help'):
            print __doc__
            sys.exit()
        if opt in ('-d', '--database'):
            db = arg
        if opt in ('-t', '--table'):
            tb = arg
        if opt in ('-c', '--country'):
            country = arg
        if opt in ('-y', '--year'):
            yr = arg
    
    app = wx.App()
    BirdUpload(None, -1, dbase=db, table=tb, presetCountry=country, presetYear=yr)
    app.MainLoop()

# End module
